<!DOCTYPE html>
<title>Credential Manager: End-to-end tests for get() with a virtual authenticator.</title>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<body>
<script type="module">
import {AuthenticatorTransport} from '/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom.m.js';
import {ACCEPTABLE_CREDENTIAL, ACCEPTABLE_CREDENTIAL_ID, authenticatorSetup, deepCopy, GET_CREDENTIAL_OPTIONS, MAKE_CREDENTIAL_OPTIONS, PUBLIC_KEY_USER} from './resources/test-inputs.js';
import {TestAuthenticatorManager} from './resources/virtual-navigator-credentials.js';

if (document.location.host != "subdomain.example.test:8443") {
  document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-get-with-virtual-authenticator.html";
  promise_test(_ => new Promise(_ => {}), "Stall tests on the wrong host.");
}

const manager = new TestAuthenticatorManager;

authenticatorSetup(manager, "CTAP2_0", () => {
  promise_test(async _ => {
    let authenticators = await manager.authenticators();
    assert_equals(authenticators.length, 1);
    let testAuthenticator = authenticators[0];
    assert_true(await testAuthenticator.generateAndRegisterKey(ACCEPTABLE_CREDENTIAL_ID, "subdomain.example.test"));
  }, "Set up the testing environment for ctap 2.0.");

  promise_test(async t => {
    let testAuthenticator = await manager.createAuthenticator();
    t.add_cleanup(async () => {
      let id = await testAuthenticator.id();
      return manager.removeAuthenticator(id);
    });
    assert_true(await testAuthenticator.generateAndRegisterKey(ACCEPTABLE_CREDENTIAL_ID, "subdomain.example.test"));
    let keys = await testAuthenticator.registeredKeys();
    assert_equals(keys.length, 1);
    var customGetAssertionOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
    var someOtherCredential = deepCopy(ACCEPTABLE_CREDENTIAL);
    someOtherCredential.id = new TextEncoder().encode("someOtherCredential");
    customGetAssertionOptions.allowCredentials = [someOtherCredential];

    return promise_rejects_dom(t, "NotAllowedError",
      navigator.credentials.get({ publicKey : customGetAssertionOptions}));
  }, "navigator.credentials.get() for unregistered device returns NotAllowedError");

  promise_test(async t => {
    // Injecting resident credentials through the mojo virtual authenticator is
    // not supported.
    var customMakeCredOptions = deepCopy(MAKE_CREDENTIAL_OPTIONS);
    customMakeCredOptions.authenticatorSelection.requireResidentKey = true;
    var residentCredential =
      await navigator.credentials.create({ publicKey : customMakeCredOptions });

    var customGetAssertionOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
    delete customGetAssertionOptions.allowCredentials;
    customGetAssertionOptions.user = PUBLIC_KEY_USER;
    return navigator.credentials.get({ publicKey : customGetAssertionOptions })
      .then(credential => assert_equals(credential.id, residentCredential.id));
  }, "navigator.credentials.get() with empty allowCredentials returns a resident credential");

  promise_test(t => {
    var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
    customGetCredentialOptions.allowCredentials.transports = [];
    return navigator.credentials.get({publicKey: customGetCredentialOptions}).then();
  }, "navigator.credentials.get() with missing transports in allowCredentials.");

  promise_test(async t => {
    const authAbortController = new AbortController();
    const authAbortSignal = authAbortController.signal;
    return navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS, signal : authAbortSignal});
  }, "navigator.credentials.get() with abort signal flag not set");

  promise_test(async t => {
    const authAbortController = new AbortController();
    const authAbortSignal = authAbortController.signal;
    authAbortController.abort();
    var promise = navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS, signal : authAbortSignal});
    return promise_rejects_dom(t, "AbortError", promise);
  }, "navigator.credentials.get() with abort signal flag set before sending request");

  promise_test(async t => {
    const authAbortController = new AbortController();
    const authAbortSignal = authAbortController.signal;
    var promise = navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS, signal : authAbortSignal});
    authAbortController.abort();
    return promise_rejects_dom(t, "AbortError", promise);
  }, "navigator.credentials.get() with abort signal flag set after sending request");

  promise_test(async t => {
    const authAbortController = new AbortController();
    const authAbortSignal = authAbortController.signal;
    let promise = await navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS, signal : authAbortSignal});
    authAbortController.abort();
    return promise;
  }, "navigator.credentials.get() with abort signal flag set after promise resolved");

  promise_test(async t => {
    let bleTestAuthenticator = await manager.createAuthenticator({
      transport: AuthenticatorTransport.BLE,
    });
    t.add_cleanup(async () => {
      let id = await bleTestAuthenticator.id();
      return manager.removeAuthenticator(id);
    });
    let bleCredential = deepCopy(ACCEPTABLE_CREDENTIAL);
    bleCredential.id = new TextEncoder().encode("bleCredential");
    bleCredential.transports = ["ble"]
    assert_true(await bleTestAuthenticator.generateAndRegisterKey(bleCredential.id, "subdomain.example.test"));
    let keys = await bleTestAuthenticator.registeredKeys();
    assert_equals(keys.length, 1);

    let customGetAssertionOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
    customGetAssertionOptions.allowCredentials = [bleCredential];
    return navigator.credentials.get({ publicKey : customGetAssertionOptions });
  }, "navigator.credentials.get() with authenticators supporting different transports");
}, {});
</script>
